/* ----------------------------------------------------------------------------

	pax.template.js Copyright (C) 2006, 2007, 2008 Mikkel Bergmann, Pointful

	Licence
	-------
	
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

	See lgpl.txt for licence details

---------------------------------------------------------------------------- */


/*
	Script: pax.template
		This PAX library can parse and render a PAX template
		
	Author:
		Mikkel Bergmann, <http://www.pointful.com>
		
	Note:
		In IE we sometimes get: "Unkown runtime error...", when we try to write html that is partially rendered,
		such as a table part or a as list item without enclosing list start tags.
		The reason is that you can not write a partial table using innerHTML with IE. This is acknowledged by microsoft:
			http://support.microsoft.com/kb/239832
		The solution is to use proper DOM methods or write a full (valid) table, ie: ensure you have properly formed HTML...
*/

var pax = pax || {};
pax.template = pax.template || {};

/*	Property: sTag
		[: Start tag - executes inline JS code.	*/
pax.template.sTag = "[:";
/*	Property: eTag
		:] End tag.	*/
pax.template.eTag = ":]";
/*	Property: vTag
		[:= Value tag.	*/
pax.template.vTag = "[:=";
/*	Property: bTag
		[:_ Before tag - runs code before template rendering. Eg: for setting up an OBJ to be accessed during rendering.	*/
pax.template.bTag = "[:_";
/*	Property: aTag
		[:. After tag - runs code after template rendering. Eg: for unobtrusive JS.	*/
pax.template.aTag = "[:.";
/*	Property: pTag
		[:p Partial tag - signifies a part of a template that can be rendered partially.	*/
pax.template.pTag = "[:p";
//	The partial end tag is the only end tag that is different; you 
//	MUST be able to discern partial parts before parsing all other tags.
/*	Property: peTag
		p:] Partial end tag - End of partial template rendering.	*/
pax.template.peTag = "p:]";
/*	Private Property: ajaxTargetID
		Used to get a unique ajax template target id, ensuring loaded templates are rendered correctly.	*/
pax.template.ajaxTargetID = 0;


/*	Method: pax.template.render
	Renders the template with given value into the target container.
	
	Parameters:
		template - String with the contents of the template
		value - Object with values as required
		target - A DOM element to render the template into
		
	Example:
		(start code)
			<div id='pax.template.render.example1'></div>
			[:.
				var value = { 
					greeting: 'Hello',
					name: 'World' 
				};
				var template = "[" + ":= greeting :" + "] [" + ":= name :" + "]!";
				pax.template.render( template, {
					value: value,
					target: pax.$('pax.template.render.example1') 
				} );
			:]
		(end)
		This will display "Hello World!" in the DIV. Note that the formatting is a bit funny, as we're using the PAX template system for this documentation as well.

	Example:
		(start code)
			<div id='pax.template.render.example2'></div>

			<button id='partial1'>Partial update 1</button>
			<button id='partial2'>Partial update 2</button>

			[:.	var myTemplate = "" +
					"[" + ":= greeting :" + "] [" + ":= name :" + "]!<br>" +
					"[" + ":p(template_stub)" +
					" Time stamp 1 is: [" + ":= time :" + "]" +
					"p:" + "]" +
					"<br>" +
					"[" + ":p(template_stub2)" +
					" Time stamp 2 is: [" + ":= time :" + "]" +
					"p:" + "] The end of the template." +
				"";

			    var myVars = {
			        greeting: 'Hello',
			        name: 'World',
					time: new Date().getTime()
			    };
				
				pax.template.render( myTemplate, {
					value: myVars, 
					target: pax.$('pax.template.render.example2') 
				} );
				
				pax.event.bindOne( pax.$('partial1'), 'click', function() {
					myVars.time = new Date().getTime();
					pax.template.render( myTemplate, { value: myVars, partial: 'template_stub' } );
				} );
				
				pax.event.bindOne( pax.$('partial2'), 'click', function() {
					myVars.time = new Date().getTime();
					pax.template.render( myTemplate, { value: myVars, partial: 'template_stub2' } );
				} );
			:]
		(end)
		This will display a sentence, and two buttons that can initiate a partial render of the template. Note that you MUST have a DOM element to render the template into, in order to use partial rendering.
		
*/
pax.template.render = function( template, args ) {
	args = pax.defaultArgs( {
		value: {},
		target: false,
		partial: []
	}, args );
	
	if( pax.util.getType( args.partial ) != 'array' )args.partial = [args.partial];
	
	if( args.partial.length > 0 ) {
		var result = pax.template.parse( template, args.value, args.partial );
			
		for( var p = 0; p < args.partial.length; p++ ) {
			pax.template.render( result[args.partial[p]], { 
				value: args.value, 
				target: pax.$(args.partial[p]) 
			} );
		}
	} else {
		var result = pax.template.parse( template, args.value );
		
		//	Note: see Comment a the start of this document if you're getting "Unkown runtime error..."
		if( args.target )args.target.innerHTML = result.html;
		else return result.html;
		
		for( var j in result.js )eval( result.js[j] );
	}
};



/*	Method: pax.template.parse
	Private method: Parses a template, and returns a dict with a HTML string, and a list with JS code to evaluate afterwards.
	You should only need to use <pax.render>, unless you're writing your own rendering method.
	
	Parameters:
		template - String with the contents of the template
		_ - Object with values as required
		
	Returns:
		{ html: string, [js1, js2, ..., jsX] }
		
	Note that this method has internal methods that you can use in a template:
		include( id ) - A method to get the value of the textarea with given id, and use it as a template
		load( url, target ) - A method to get a template from a url, and render it after the parent template has been rendered

	Example:
		(start code)
			<div id='pax.template.parse.example1'></div>
			[:.
				var myVars = { 
					greeting: 'Hello',
					name: 'World' 
				};
				var result = pax.template.parse( "[" + ":= greeting :" + "] [" + ":= name :" + "]!", myVars );
				pax.$('pax.template.parse.example1').innerHTML = pax.util.toString( result );
			:]
		(end)
		This will set result as {"html":"Hello World!","js":[]}, and then display that in the DIV.
		
	Example:
		(start code)
			<div id='pax.template.parse.example2'></div>
			[:.
				var myVars = { 
					greeting: 'Hello',
					name: 'World' 
				};
				var result = pax.template.parse( "[" + ":= greeting :" + "] [" + ":= name :" + "]! <span id='pax.template.parse.example2.update'></span>", myVars );
				pax.$('pax.template.parse.example2').innerHTML = pax.util.toString( result.html );
			:]
		(end)
		This will set result as {"html":"Hello World!","js":[]}, and then display that in the DIV.
*/
pax.template.parse = function( template, value, partial ) {
	template = template.replace(/[\n\r]/g,"");						// Remove new lines
	template = template.replace(/&lt;/g,"<").replace(/&gt;/g,">");	// Add angle brackets, as innerHTML automatically converts < to &lt; and > to &gt;

	//	Bring in the modifiers as localised variables, if necessary.
	if( ! (typeof _def === 'function') )pax.scope( pax.template.modifier );
	pax.scope( value );		//	Scope the value object

	// First we split by the partial tags, and re-create the template without partial tags in it.
	var partialBits = {};
	var partialResult = [];
	var endStr = template.split( pax.template.peTag );				//	split by partial end tag
	
	for( var s = 0; s < endStr.length; s++ ) {
		var sPartialTagIndex = endStr[s].indexOf( pax.template.pTag );
		if( sPartialTagIndex > -1 ) {			// 	If we have a start partial tag in this part
			var startStr = endStr[s].split( pax.template.pTag );	//	Split by start partial tag
			
			//	This assumes we have the partial tag here, in brackets, eg: "(partial_template_bit)"
			var partialFound = startStr[1].substring( 1, startStr[1].indexOf( ')' ) );
			
			//	We parse the partial string, so it can be unique in the DOM
			partialFound = pax.template.parse( partialFound ).html;
			var foundBit = startStr[1].substring( startStr[1].indexOf( ')' ) + 1, startStr[1].length );
			if( pax.util.hasValue( partialFound, partial ) ) {
				partialBits[partialFound] = foundBit;
			}
			
			partialResult.push( startStr[0] );
			partialResult.push( "<span id='" + partialFound + "'>" + foundBit + "</span>" );
		}
		else partialResult.push( endStr[s] );
	}

	//	 Dodgy - return something different, if we've specified a partial!
	if( typeof( partial ) != 'undefined' )return partialBits;
	
	template = partialResult.join('');

	// First we split by the tags
	var splitResult = [];
	var startStr = template.split( pax.template.sTag );				//	Split by start tag
	for( var s = 0; s < startStr.length; s++ ) {
		if( startStr[s].indexOf( pax.template.eTag ) > -1 ) {		// 	If we have an end tag
			var endStr = startStr[s].split( pax.template.eTag );	//	Split by end tag
			var addStart = true;
			for( var e = 0; e < endStr.length; e++ ) {
				//	Re-add the start tag for parts that is not plain HTML.
				splitResult.push( (addStart)? pax.template.sTag + endStr[e] : endStr[e] );
				addStart = false;
			}
		}
		else splitResult.push( startStr[s] );
	}

	// Next we push the parts in the result array, by tags
	var result = [];
	var runBefore = [];
	var runAfter = [];
	for( var r = 0; r < splitResult.length; r++ ) {
		var part = splitResult[r];
		if( part.indexOf( pax.template.vTag ) > -1 )		result.push( "parsedParts.push(" + part.replace( pax.template.vTag, '' ) + ");" );
		else if( part.indexOf( pax.template.aTag ) > -1 )	runAfter.push( part.replace( pax.template.aTag, '' ) );
		else if( part.indexOf( pax.template.bTag ) > -1 )	runBefore.push( part.replace( pax.template.bTag, '' ) );
		else if( part.indexOf( pax.template.sTag ) > -1 )	result.push( part.replace( pax.template.sTag, '' ) );
		else 												result.push( "parsedParts.push(\"" + part.replace(/\"/g,"\\\"") + "\");" );
	}
	
	result.push( "parsedParts.join('');" );
	
	//	Pushes a template into this template. Note: This function is local to the parse function and will override a global push function in the template scope only.
	var push = function( template, pushValue ) {



		//	NOTE: This may be the issue: parsing here would not necessarily preserve the values???

		var incResult = pax.template.parse( template, pushValue );
		
		
		
		

		//	This will push the value "in front" of the javascript part, thus preserving the values!
		//	Note: nodes or object pointers won't work, as we're serialising it...
		//	TODO: Don't serialise somehow, especially with nodes...
		//	for( var j in incResult.js )runAfter.push( "var value = " + pax.util.toString(pushValue) + ";" + incResult.js[j] );
		
		for( var j in incResult.js )runAfter.push( "var value = " + pax.util.toString(pushValue) + ";" + incResult.js[j] );
		
		// In IE, if you try to EVAL plain HTML, it errors (works ok in FF though). So, we add a list around it, and works!
		var incResValue = eval( [ incResult.html ] );
		
		return incResValue;
	};

	//	Includes a locally defined template (via innerHTML). Note: This function is local to the parse function and will override a global include function in the template scope only.
	var include = function( id ) {
		// var included = document.getElementById( id ).innerHTML;
		var included = document.getElementById( id ).value;
		var incResult = pax.template.parse( included, value );
		for( var j in incResult.js )runAfter.push( incResult.js[j] );
		// In IE, if you try to EVAL plain HTML, it errors (works ok in FF though). So, we add a list around it, and works!
		var incResValue = eval( [ incResult.html ] );
		return incResValue;
	};

	//	Loads a template via an ajax call, and renders it after parent template has been rendered.
	var load = function( url, target ) {
		pax.template.ajaxTargetID += 1;
		target = (typeof target != 'undefined')? target : url + '_target_' + pax.template.ajaxTargetID;	// Ensure we have a unique target
		runAfter.push( "pax.cache.template.loadTemplate( '"+url+"', false, function( templateUrl ) { pax.template.render( pax.cache.get( templateUrl ), { target: pax.$('"+target+"') } ) } );" );
		return "<span id='" + target + "'></span>";
	};
	
	var parsedParts = [];
	for( var j in runBefore )eval( runBefore[j] );
	
	/*	WARNING: This WILL cause 'invalid XML tag syntax' in FF, and "Operation Aborted" in IE, if result is invalid JS.

		. This can occur from unclosed closures, or other related problems.
		. Ie: this will occur if the result is not enclosed in "parsedParts.push(..." commands.
			-> simply output the result here, to see; basically we should have valid javascript here.
			-> firebug doesn't log the whole string, so you will need to use a textarea.
	*/
	var htmlResult = eval( result.join( '\n' ) );

	return { html: htmlResult, js: runAfter }
};